#pragma once
#include <vector>
#include <fstream>
#include <sstream>
#include <iostream>
#include <glm/glm.hpp>
#include "mesh.h"
#include "util.h"
using namespace std;

class Model {
private:
	vector<glm::vec3> buffer_pos;//vertex的位置
	vector<glm::vec2> buffer_tex;//vertex的texture coordinate
	vector<glm::vec3> buffer_nor;//vertex的normal vector 
	vector<glm::vec3> pos;//这个和后面的两个都只是临时变量
	vector<glm::vec2> tex;
	vector<glm::vec3> nor;

public:
	std::string name;
	Shader* shader;//module 需要设置shader程序
	int texture = -1;//texture的id
	vector<Mesh> meshes;//mesh里面保存了各个面的数据（包括三个一组的点的坐标，texture coordinate ，normal vector）
	//后面是一些保存的变换类型
	glm::vec3 scale{ 1,1,1 };
	glm::vec3 translate{ 0,0,0 };
	glm::vec4 rotate_x{ 1,0,0,0 };
	glm::vec4 rotate_y{ 0,1,0,0 };
	glm::vec4 rotate_z{ 0,0,1,0 };
	glm::vec4 rotate_axis{ 1,0,0,0 };
	/*vector<glm::vec3>& getpos() { return buffer_pos; }
	vector<glm::vec2>& gettex() { return buffer_tex; }*/
	Model(const char* path) {//加载模型
		this->name = path+7;
		readObj_FILE(path);
		//readObj_fstream(path);
	}

	Model(const char* path, Shader* shader) {//加载模型并设置shader
		this->name = path + 7;
		readObj_FILE(path);
		//readObj_fstream(path);
		this->shader = shader;
	}

	void readObj_fstream(const char* path) {//解析并加载模型
		ifstream file(path, ios::in);
		if (!file.is_open())
		{
			cerr << "error in open model file:" << path << endl;
			exit(-1);
		}
		string line, type, preType = "";
		float v1, v2, v3;
		while (getline(file, line)) {//这是自己解析的3d文件的格式
			istringstream strStream(line);//每一行解析为一个string类型，然后逐个解析数据
			strStream >> type;
			if (type == "v") {//v表示的是顶点坐标
				if (preType == "f") {//如果之前的类型是一个面，就将这个面的数据放到mesh中
					meshes.push_back(Mesh(pos, tex, nor));
					vector<glm::vec3>().swap(pos);//清空这些数组信息，为下一个面做准备
					vector<glm::vec2>().swap(tex);
					vector<glm::vec3>().swap(nor);
				}
				//如果只是一个点，那么直接将三个顶点的坐标放到buffer_pos中
				strStream >> v1 >> v2 >> v3;
				buffer_pos.push_back(glm::vec3(v1, v2, v3));
				preType = "v";//告知下一个数据，上一次的数据类型是v
			}
			else if (type == "vt") {//如果是vertex texture coordinate
				strStream >> v1 >> v2;
				buffer_tex.push_back(glm::vec2(v1, v2));//将textuer coordinate放到buffer_tex中
				preType = "vt";
			}
			else if (type == "vn") {//类似操作，normal vector
				strStream >> v1 >> v2 >> v3;
				buffer_nor.push_back(glm::vec3(v1, v2, v3));
				preType = "vn";
			}
			else if (type == "f") {//这个是一个面？
				vector<glm::ivec3> indices;
				int i1, i2, i3;
				char slash;//就是占个位
				string buffer_vertex_data;
				while (strStream >> i1 >> slash >> i2 >> slash >> i3) {//解析出面的三个点的索引
					indices.push_back({ i1,i2,i3 });//放到indice数组中
				}
				//从buffer中加载顶点对应的坐标和texture ，normal vector
				for (int i = 1; i < indices.size() - 1; ++i) {
					pos.push_back(buffer_pos.at(indices.at(0)[0] - 1));
					tex.push_back(buffer_tex.at(indices.at(0)[1] - 1));
					nor.push_back(buffer_nor.at(indices.at(0)[2] - 1));
					for (int j = 0; j < 2; ++j) {
						pos.push_back(buffer_pos.at(indices.at(i + j)[0] - 1));
						tex.push_back(buffer_tex.at(indices.at(i + j)[1] - 1));
						nor.push_back(buffer_nor.at(indices.at(i + j)[2] - 1));
					}
				}
				preType = "f";
			}
		}
		file.close();
		meshes.push_back(Mesh(pos, tex, nor));//将面加到mesh中
	}

	void readObj_FILE(const char* path) {//这个和上面不同的是读取的是文本格式的文件
		FILE* fp;
		if (fopen_s(&fp, path, "r") != 0) {
			fprintf(stderr, "error in read obj file");
			exit(-1);
		}
		char buffer[1024];
		float v1, v2, v3;
		int i1, i2, i3;
		char type[4], preType[4] = "";
		while (fgets(buffer, sizeof(buffer), fp) != nullptr) {
			sscanf_s(buffer, "%s", &type, 4);
			if (!strcmp(type, "v")) {
				if (!strcmp(preType, "f")) {
					meshes.push_back(Mesh(pos, tex, nor));
					vector<glm::vec3>().swap(pos);
					vector<glm::vec2>().swap(tex);
					vector<glm::vec3>().swap(nor);
				}
				sscanf_s(buffer, "%s %f %f %f", type, 4, &v1, &v2, &v3);
				buffer_pos.push_back(glm::vec3(v1, v2, v3));
				strcpy_s(preType, sizeof(preType) / sizeof(char), "v");
			}
			else if (!strcmp(type, "vt")) {
				sscanf_s(buffer, "%s %f %f", type, 4, &v1, &v2);
				buffer_tex.push_back(glm::vec2(v1, v2));
				strcpy_s(preType, sizeof(preType) / sizeof(char), "vt");
			}
			else if (!strcmp(type, "vn")) {
				sscanf_s(buffer, "%s %f %f %f", type, 4, &v1, &v2, &v3);
				buffer_nor.push_back(glm::vec3(v1, v2, v3));
				strcpy_s(preType, sizeof(preType) / sizeof(char), "vn");
			}
			else if (!strcmp(type, "f")) {
				vector<glm::ivec3> indices;
				char* ptr = nullptr;
				char* token = strtok_s(buffer, " \n", &ptr);
				token = strtok_s(nullptr, " \n", &ptr);//去除开头的f
				while (token) {
					sscanf_s(token, "%d/%d/%d", &i1, &i2, &i3);
					indices.push_back(glm::ivec3(i1, i2, i3));
					token = strtok_s(nullptr, " \n", &ptr);
				}
				for (int i = 1; i < indices.size() - 1; ++i) {
					pos.push_back(buffer_pos.at(indices.at(0)[0] - 1));
					tex.push_back(buffer_tex.at(indices.at(0)[1] - 1));
					nor.push_back(buffer_nor.at(indices.at(0)[2] - 1));
					for (int j = 0; j < 2; ++j) {
						pos.push_back(buffer_pos.at(indices.at(i + j)[0] - 1));
						tex.push_back(buffer_tex.at(indices.at(i + j)[1] - 1));
						nor.push_back(buffer_nor.at(indices.at(i + j)[2] - 1));
					}
				}
				strcpy_s(preType, sizeof(preType) / sizeof(char), "f");
			}
		}
		fclose(fp);
		meshes.push_back(Mesh(pos, tex, nor));
	}

	void bindData() {//绑定数据，就是VAO保存数据的配置，VBO管理属性数据
		for (auto& mesh : meshes) {		//注意这里需要引用传递，否则mesh中bindData生成的VAO、VBO不能保存
			mesh.bindData();
		}
	}

	void bindShader(Shader* shader) {//绑定program。就是设置使用的shader program
		this->shader = shader;
	}

	void bindTexture(const char* path) {//绑定texture ,这里指定的是texture的id
		texture = Util::loadTexture(path);
	}

	//用模型绑定的着色器绘制 (glPolygonMode：GL_FILL)
	void draw() {//这里为什么不对VAO也进行同样的绑定再解绑的操作？
		if (this->texture > 0) {
			//glActiveTexture(this->texture);
			glBindTexture(GL_TEXTURE_2D, this->texture);
		}
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		for (auto mesh : meshes) {
			mesh.draw(this->shader->id);
		}
		glBindTexture(GL_TEXTURE_2D, 0);
	}

	//使用参数指定的着色器绘制
	void draw(GLuint programId) {
		if (this->texture > 0) {
			glActiveTexture(this->texture);
			glBindTexture(GL_TEXTURE_2D, this->texture);
		}
		for (auto mesh : meshes) {
			mesh.draw(programId);
		}
	}

	glm::mat4 apply_rotate(glm::mat4 modelMat) {//对输入的矩阵施加模型中保存的循环运算后返回，似乎是的
		modelMat = glm::rotate(modelMat, glm::radians(this->rotate_x.w), glm::vec3(this->rotate_x));
		modelMat = glm::rotate(modelMat, glm::radians(this->rotate_y.w), glm::vec3(this->rotate_y));
		modelMat = glm::rotate(modelMat, glm::radians(this->rotate_z.w), glm::vec3(this->rotate_z));
		modelMat = glm::rotate(modelMat, glm::radians(this->rotate_axis.w), glm::vec3(this->rotate_axis));//自定义轴？
		return modelMat;
	}

	void shaderSetMatrixes(glm::mat4 model, glm::mat4 view, glm::mat4 projection) {//设置变换矩阵，设置到shader中。
		this->shader->use();
		this->shader->setMat4("model", model);
		this->shader->setMat4("view", view);
		this->shader->setMat4("projection", projection);
	}
};